package com.powerwyx.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powerwyx.shortlink.admin.common.biz.user.UserContext;
import com.powerwyx.shortlink.admin.common.convention.errorcode.BaseErrorCode;
import com.powerwyx.shortlink.admin.common.convention.exception.ClientException;
import com.powerwyx.shortlink.admin.common.convention.exception.ServiceException;
import com.powerwyx.shortlink.admin.common.convention.result.Result;
import com.powerwyx.shortlink.admin.mapper.GroupMapper;
import com.powerwyx.shortlink.admin.mapper.GroupUniqueMapper;
import com.powerwyx.shortlink.admin.pojo.dto.SortGroupDTO;
import com.powerwyx.shortlink.admin.pojo.dto.UpdateGroupDTO;
import com.powerwyx.shortlink.admin.pojo.entity.Group;
import com.powerwyx.shortlink.admin.pojo.entity.GroupUnique;
import com.powerwyx.shortlink.admin.pojo.vo.GroupVO;
import com.powerwyx.shortlink.admin.remote.ShortLinkActualRemoteService;
import com.powerwyx.shortlink.admin.remote.vo.ShortLinkCountVO;
import com.powerwyx.shortlink.admin.service.GroupService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.powerwyx.shortlink.admin.common.constant.RedisConstant.LOCK_GROUP_CREATE_KEY;

/**
 * @author 17585
 * @description 针对表【t_group】的数据库操作Service实现
 * @createDate 2024-07-04 19:04:04
 */
@Service
public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group>
        implements GroupService {
    @Autowired
    private GroupMapper groupMapper;

    @Autowired
    private GroupUniqueMapper groupUniqueMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Value("${short-link.group.max-num}")
    private Integer groupMaxNum;

   @Autowired
   private ShortLinkActualRemoteService shortLinkActualRemoteService;

   @Autowired
   private RBloomFilter<String> gidRegisterCachePenetrationBloomFilter;


    private String saveGroupUniqueReturnGid() {
        String gid = RandomUtil.randomString(6);
        if (gidRegisterCachePenetrationBloomFilter.contains(gid)) {
            return null;
        }
        GroupUnique groupUniqueDO = GroupUnique
                                .builder()
                                .gid(gid)
                                .build();
        try {
            // 线程 A 和 B 同时生成了相同的 Gid，会被数据库的唯一索引校验触发异常
            // 流程不能被这个异常阻断，需要获取异常重试
            groupUniqueMapper.insert(groupUniqueDO);
        } catch (DuplicateKeyException e) {
            return null;
        }
        return gid;
    }

    /**
     * 新增分组
     */
    @Override
    public void saveGroup(String name) {
        saveGroup(name, UserContext.getUsername());
    }


    /**
     * 新增分组
     *
     * @param name
     */
    @Override
    public void saveGroup(String name, String username) {
        if (name == null) {
            throw new ClientException(BaseErrorCode.PARAMS_ERROR);
        }
        RLock lock = redissonClient.getLock(LOCK_GROUP_CREATE_KEY + username);
        lock.lock();
        try {
            List<Group> list = list(new QueryWrapper<Group>()
                    .eq("username", username)
                    .eq("del_flag", 0));
            if (CollUtil.isNotEmpty(list) && list.size() == groupMaxNum) {
                throw new ClientException(String.format("已超出最大分组数量:%d",groupMaxNum));
            }
            int retryCount = 0;
            int maxRetries = 10;
            String gid = null;
            // 如果布隆过滤器满了，可能会一直重复，所以这里加一个限制条件
            while (retryCount < maxRetries) {
                gid = saveGroupUniqueReturnGid();
                if (StrUtil.isNotEmpty(gid)) {
                    Group group = Group.builder()
                            .gid(gid)
                            .sortOrder(0)
                            .username(username)
                            .name(name)
                            .build();
                    baseMapper.insert(group);
                    gidRegisterCachePenetrationBloomFilter.add(gid);
                    break;
                }
                retryCount++;
            }
            if (StrUtil.isEmpty(gid)) {
                throw new ServiceException("生成分组标识频繁");
            }
        } finally {
            lock.unlock();
        }
    }



    /**
     * 查询分组集合
     *
     * @return
     */
    @Override
    public List<GroupVO> getGroups() {
        String username = UserContext.getUsername();
        List<Group> groupList = list(new QueryWrapper<Group>()
                .eq("username", username)
                .eq("del_flag", 0)
                .orderByDesc("sort_order")
                .orderByDesc("update_time"));
        Result<List<ShortLinkCountVO>> listResult =
                shortLinkActualRemoteService.getGroupShortLinkCount(groupList.stream().map(Group::getGid).collect(Collectors.toList()));

        List<GroupVO> groupVOList = BeanUtil.copyToList(groupList, GroupVO.class);

        groupVOList.forEach(groupVO -> {
            Optional<ShortLinkCountVO> first = listResult.getData().stream()
                    .filter(item -> Objects.equals(item.getGid(), groupVO.getGid()))
                    .findFirst();
            first.ifPresent(shortLinkCountVO -> groupVO.setShortLinkCount(shortLinkCountVO.getShortLinkCount()));

        });

        return groupVOList;
    }


    /**
     * 修改分组信息
     *
     * @param updateGroupDTO
     */
    @Override
    public void updateGroup(UpdateGroupDTO updateGroupDTO) {
        if (updateGroupDTO == null) {
            throw new ClientException(BaseErrorCode.PARAMS_ERROR);
        }
        Group group = new Group();
        group.setName(updateGroupDTO.getName());
        boolean isSuccess = update(group, new QueryWrapper<Group>()
                .eq("del_flag", 0)
                .eq("gid", updateGroupDTO.getGid())
                .eq("username", UserContext.getUsername()));
        if (!isSuccess) {
            throw new ClientException(BaseErrorCode.OPERATION_ERROR);
        }
    }


    /**
     * 删除短连接分组
     *
     * @param gid
     */
    @Override
    public void deleteGroup(String gid) {
        if (gid == null) {
            throw new ClientException(BaseErrorCode.PARAMS_ERROR);
        }
        Group group = new Group();
        group.setDelFlag(1);
        boolean isSuccess = update(group, new QueryWrapper<Group>()
                .eq("username", UserContext.getUsername())
                .eq("gid", gid)
                .eq("del_flag", 0));

        if (!isSuccess) {
            throw new ClientException(BaseErrorCode.OPERATION_ERROR);
        }
    }


    /**
     * 排序短连接分组
     *
     * @param sortGroupDTOList
     */
    @Override
    public void sortGroup(List<SortGroupDTO> sortGroupDTOList) {
        if (sortGroupDTOList.isEmpty()) {
            throw new ClientException(BaseErrorCode.PARAMS_ERROR);
        }
        sortGroupDTOList.forEach(sortGroupDTO -> {
            Group group = Group.builder()
                    .sortOrder(sortGroupDTO.getSortOrder())
                    .build();
            boolean isSuccess = update(group, new QueryWrapper<Group>()
                    .eq("username", UserContext.getUsername())
                    .eq("gid", sortGroupDTO.getGid())
                    .eq("del_flag", 0));

            if (!isSuccess) {
                throw new ClientException(BaseErrorCode.OPERATION_ERROR);
            }
        });
    }
}




